/**
 * @file my_uart.c
 * @author sevenfite (lin481399413@163.com) 东莞理工机器人创新实验室
 * @brief 串口接收数据处理
 * @version 0.1
 * @date 2024-11-10
 * @verbatim  
 *   1.实现了printf函数的重定向
 *   2.实现了串口接收数据处理
 *      (1)支持中断接收，需要有结束符\r\n
 *      (2)支持DMA接收，不需要结束符
 * @endverbatim
 * @example
 * @code
 *   定义变量和函数：
 *   char uart1_rx_buf[100];//定义接收缓冲区,使用静态分配内存时需要
 *   USART_Data usart_Data_handle;//定义串口数据结构体
 *   void uart1_callback(USART_Data *me){//可选,定义串口接收完成后执行的函数
 *     //不能使用阻塞函数，包括阻塞型串口发送函数
 *     //do something
 *   }
 *   main函数中的初始化：
 *   //初始化串口接收数据结构体,使用静态分配内存,使用DMA,有回调函数,如果不使用回调,传NULL
 *   USART_DataTypeInit(&usart_Data_handle, &huart1, (uint8_t *)uart1_rx_buf, sizeof(uart1_rx_buf), DMA_MODE, uart1_callback);
 * @endcode
 * 使用方式：
 *     (1)可以使用回调函数自行处理接收到的数据，但是要注意处理的时间不能过长，否则会影响串口接收
 *     (2)可以使用USART_DataIsReceived(&usart_Data_handle)判断是否接收到数据,
 *        如果接收到数据，使用USART_DataResetReceivedFlag(&usart_Data_handle)清除接收完成标志位,
 *        以便下次接收数据
 *     (3)使用USART_DataGetReceivedLen(&usart_Data_handle)获取接收到的数据长度，
 *        使用USART_DataCleanReceivedLen(&usart_Data_handle)清除接收到的数据长度,
 *        以便下次使用
 *     (4)使用USART_GetData(&usart_Data_handle)获取接收到的数据的指针,在动态分配内存时比较有用(静态也能用)
 *     (5)使用轮询的方式，轮询USART_DataIsReceived(&usart_Data_handle)判断是否接收到数据
 *     (6)推荐使用静态分配加DMA的方式,静态分配更可控,DMA接收效率更高
 *     (7)此文件没有包含串口初始化函数,需要自行完成初始化配置(可以使用CubeMX生成的初始化函数)
 *        串口的接收等函数使用HAL库的函数,所以需要有串口句柄的传入
 * @note 
 *     新增了DMA循环模式结合环形缓冲区的方式接收数据
 *     使用uartDataNotEmpty()函数判断串口接收缓冲区是否有数据可读，
 *     使用uartGetData()函数获取串口接收缓冲区中的数据
 *     当缓冲区满时，会把最旧的一帧数据删除掉，但是不会关闭DMA
 * @warning 没有支持动态内存分配，没有自动检测DMA循环模式是否开启
 *       此版本只是参考，很多功能并不全面
 *       对于线程安全问题，不能做到完全保证
 * @note 
 *  修改说明：2025/7/10
 *  增加了初始化函数的返回值，并且规定不能同时存在两个相同的串口句柄
 * @copyright Copyright (c) 2024
 * 
 */
#include "my_uart.h"
#if 1 //支持printf
/*注意，这里的printf是有输出缓冲的，输出的字符串要加上\r\n才会输出到串口
如果想要立即输出，可以使用fflush(stdout)刷新缓冲区
*/
#ifdef __GNUC__ //使用gcc编译器
int _write (int fd, char *pBuffer, int size)
{
    (void)fd;//去除warning
    for (int i = 0; i < size; i++)
    {
        while((Printf_UART->SR&0X40)==0);//等待上一次串口数据发送完成
        Printf_UART->DR = (uint8_t) pBuffer[i];       //写DR,串口1将发送数据
    }
    return size;
}
#else 
/*摘取自正点原子例程*/
#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");  /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式，否则部分例程可能出现半主机模式 */

#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

#endif

/* 不使用半主机模式，至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
    x = x;
}

char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}


/* FILE 在 stdio.h里面定义. */
FILE __stdout;

/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((Printf_UART->SR & 0X40) == 0);     /* 等待上一个字符发送完成 */

    Printf_UART->DR = (uint8_t)ch;             /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
#endif

#endif
//没有初始化串口的函数，请在main函数中调用MX_USARTx_UART_Init()初始化串口
USART_Data *usartDataHead_handle = NULL;  // 串口数据结构体链表头
static int USART_DataAttach(USART_Data *this);

#ifdef RING_BUFFER
//队列的实现
static void queue_init(struct RX_Frame_Len_Queue_t *queue){
  queue->front = 0;
  queue->rear = 0;
}
static void queue_push(struct RX_Frame_Len_Queue_t *queue, uint32_t len){
  queue->frame_len[queue->rear] = len;
  queue->rear = (queue->rear + 1) % RX_FRAME_LEN_QUEUE_SIZE;
}
static uint32_t queue_pop(struct RX_Frame_Len_Queue_t *queue){
  uint32_t len = queue->frame_len[queue->front];
  queue->front = (queue->front + 1) % RX_FRAME_LEN_QUEUE_SIZE;
  return len;
}
static uint8_t queue_is_empty(struct RX_Frame_Len_Queue_t *queue){
  return queue->front == queue->rear;
}
static uint8_t queue_is_full(struct RX_Frame_Len_Queue_t *queue){
  return (queue->rear + 1) % RX_FRAME_LEN_QUEUE_SIZE == queue->front;
}
static uint32_t queue_peek(struct RX_Frame_Len_Queue_t *queue){
  if(queue_is_empty(queue)) return 0;
  return queue->frame_len[queue->front];
}
/**
 * @brief 先入先出获取环形缓冲区中的一帧数据
 * 
 * @param me 
 * @param data 用于存放数据的指针
 * @param len 存放数据的指针最大的长度
 * @return int8_t 实际获取的数据长度
 */
uint32_t uartGetData(USART_Data *me,uint8_t * data,uint32_t len){
  if(me==NULL||len==0||queue_peek(&me->rx_frame_len_queue)>len) return -1;
  //关中断
  __disable_irq();
  uint32_t ret = lwrb_read(&me->ringBuffer, data, queue_pop(&me->rx_frame_len_queue));
  //开中断
  __enable_irq();
  return ret;
}
/**
 * @brief 获取串口数据缓冲区是否为非空
 * 
 * @param me 
 * @return int8_t 0：空 1：非空，有数据
 */
int8_t uartDataNotEmpty(USART_Data *me){
  return !queue_is_empty(&me->rx_frame_len_queue);
}
#endif

#if USART_MALLOC == 1
#include <stdlib.h>
/**
 * @brief
 * @note  使用动态分配内存为接收缓冲区分配内存，开启中断接收/开启DMA接收
 * @param  this 指向自己的指针
 * @param huart 串口句柄
 * @param rxSize_Max 接收缓冲区大小
 * @param receiveMode IT_MODE:中断接收
 * DMA_MODE:DMA接收(中断接收要求有结束符\r\n，DMA接收不要求)
 * @param callback 中断接收完成后执行的函数，不需要可以传NULL
 * @warning 请勿在回调函数中执行过久的时间，否则会影响串口接收，包括阻塞性的串口发送函数
 * @note 波特率高的情况下，建议使用DMA接收
 * @return 0:成功,1:串口句柄已存在或接收结构体已初始化过,2:内存分配失败
 */

uint8_t USART_DataTypeInit(USART_Data *this, UART_HandleTypeDef *huart,
                        uint16_t rxSize_Max, uint8_t receiveMode,uartCallBack callback) {
  USART_Data* target = usartDataHead_handle;
  while(target) {
    if(target->huart == huart) return 1;	//表示已经注册
    target = target->next;
  }
  //分配内存
  this->usart_rx_buf = (uint8_t *)malloc(rxSize_Max);
  if (this->usart_rx_buf == NULL) return 2; //内存分配失败
  int ret = USART_DataAttach(this);
  if (ret != 0) {
    free(this->usart_rx_buf); //如果已经注册过了，释放内存
    return 1; //表示已经注册
  }

  this->huart = huart;
  this->rxSize_Max = rxSize_Max;
  this->callback = callback;

  if (receiveMode==IT_MODE) {
  HAL_UART_Receive_IT(this->huart, &this->rxBuffer, 1);
  } else if(receiveMode==DMA_MODE){
  HAL_UARTEx_ReceiveToIdle_DMA(this->huart, this->usart_rx_buf, this->rxSize_Max);
  __HAL_DMA_DISABLE_IT(
    this->huart->hdmarx,
    DMA_IT_HT);  // 关闭半传输中断,防止半传输中断影响接收完成标志位和接收长度
  }
  return 0; //成功
}
#else

/**
 * @brief 串口结构体初始化
 * @note  使用静态分配内存为接收缓冲区分配内存，要自己创建一个全局变量，开启中断接收/开启DMA接收
 * @param  this 指向自己的指针，需要自行创建
 * @param huart 串口句柄
 * @param rxBuffer 接收缓冲区，要求大小为rxSize_Max，自己定义的全局变量
 * @param rxSize_Max 接收缓冲区大小
 * @param receiveMode IT_MODE:中断接收 DMA_MODE：DMA接收(中断接收要求有结束符\r\n，DMA接收不要求)
 * @param callback 中断接收完成后执行的函数，不需要可以传NULL
 * @warning 请勿在回调函数中执行过久的时间，否则会影响串口接收，包括阻塞性的串口发送函数
 * @note 波特率高的情况下，建议使用DMA接收
 * @return 0:成功,1:串口句柄已存在或接收结构体已初始化过
 */
uint8_t USART_DataTypeInit(USART_Data *this, UART_HandleTypeDef *huart,uint8_t* rxBuffer,
						uint16_t rxSize_Max,UsartMode receiveMode,uartCallBack callback)
{
  USART_Data* target = usartDataHead_handle;
  while(target) {
    if(target->huart == huart) return 1;	//表示已经注册
    target = target->next;
  }
  int ret = USART_DataAttach(this);
  if (ret != 0) return 1; //表示已经注册

  this->huart = huart;
  this->rxSize_Max = rxSize_Max;
  this->usart_rx_buf = rxBuffer;
  this->callback = callback;

  this->receiveMode = receiveMode;

  if (receiveMode==IT_MODE) {
  HAL_UART_Receive_IT(this->huart, &this->rxBuffer, 1);
  } else if(receiveMode==DMA_MODE){
  HAL_UARTEx_ReceiveToIdle_DMA(this->huart, this->usart_rx_buf, this->rxSize_Max);
  __HAL_DMA_DISABLE_IT(this->huart->hdmarx , DMA_IT_HT);  // 关闭半传输中断,防止半传输中断影响接收完成标志位和接收长度
  }
#ifdef RING_BUFFER
  else if(receiveMode == CIRCULAR_DMA_MODE) {
    queue_init(&this->rx_frame_len_queue);
    // this->rx_RingBufferOverflowFlag = 0;
    lwrb_init(&this->ringBuffer, this->usart_rx_buf, this->rxSize_Max);
    this->lastFrameEnd = this->usart_rx_buf;
    HAL_UARTEx_ReceiveToIdle_DMA(this->huart, this->usart_rx_buf, this->rxSize_Max);
    __HAL_DMA_DISABLE_IT(this->huart->hdmarx , DMA_IT_HT);  // 关闭半传输中断,防止半传输中断影响接收完成标志位和接收长度
  }
#endif
  return 0; //成功
}
#endif
/**
 * @brief 将该结构体添加到链表中
 * @param  this 指向自己的指针
 * @return -1表示已经注册，0表示成功注册
 */
static int USART_DataAttach(USART_Data *this)
{
	USART_Data* target = usartDataHead_handle;
	while(target) {
		if(target == this) return -1;	//表示已经注册
		target = target->next;
	}
	this->next = usartDataHead_handle;
	usartDataHead_handle = this;
	return 0;
}

/**
 * @brief       串口数据接收中断回调函数
                数据处理在这里进行（中断接收模式使用）
 * @param       huart:串口句柄
 * @retval      无
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  USART_Data* this = usartDataHead_handle;
  while(this) {
    if (huart->Instance == this->huart->Instance) {
      if ((this->usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
      {
        if (this->usart_rx_sta & 0x4000) /* 接收到了0x0d（即回车键） */
        {
          if (this->rxBuffer !=
              0x0a) /* 接收到的不是0x0a（即不是换行键） */
          {
            this->usart_rx_sta = 0; /* 接收错误,重新开始 */
          } else /* 接收到的是0x0a（即换行键） */
          {
            this->usart_rx_sta |= 0x8000; /* 接收完成了 */
            if(this->callback != NULL){//执行回调函数
              this->callback((void*)this);
            }
          }
        } else /* 还没收到0X0d（即回车键） */
        {
          if (this->rxBuffer == 0x0d)
            this->usart_rx_sta |= 0x4000;
          else {
            this->usart_rx_buf[this->usart_rx_sta & 0X3FFF] =
                this->rxBuffer;
            this->usart_rx_sta++;

            if (this->usart_rx_sta > (this->rxSize_Max - 1)) {
              this->usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
            }
          }
        }
      }
      HAL_UART_Receive_IT(this->huart, &this->rxBuffer, 1);
    }
    this = this->next;
  }
}
/// @brief 串口空闲中断回调函数（DMA模式使用）
/// @param huart 串口句柄
/// @param Size 接收到的数据长度
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
  USART_Data* this = usartDataHead_handle;
  while(this) {
    if (huart->Instance == this->huart->Instance) {
#ifdef RING_BUFFER
    if (this->receiveMode == CIRCULAR_DMA_MODE) {
      if(HAL_UARTEx_GetRxEventType(this->huart) == HAL_UART_RXEVENT_IDLE){
        //记录帧长度
        int16_t size = Size + this->usart_rx_buf - this->lastFrameEnd;
        if(size < 0) size += this->rxSize_Max; //处理环形缓冲区的情况
        int writeReadyLen = lwrb_get_free(&this->ringBuffer);
        while(size > writeReadyLen) {
          //环形缓冲区溢出
          // this->rx_RingBufferOverflowFlag = 1;
          //DMA已经覆盖了信息，只能把旧的数据丢弃
          int len = queue_pop(&this->rx_frame_len_queue);
          lwrb_skip(&this->ringBuffer, len);
          writeReadyLen = lwrb_get_free(&this->ringBuffer);
        }
        //更新环形缓冲区
        lwrb_advance(&this->ringBuffer, size);
        queue_push(&this->rx_frame_len_queue, size);
        this->lastFrameEnd = this->usart_rx_buf + Size;      
      }   
    }
    else{
#endif
      this->usart_rx_sta = Size;
      this->usart_rx_sta |= 0x8000;  // 接收完成
      HAL_UARTEx_ReceiveToIdle_DMA(this->huart,this->usart_rx_buf,this->rxSize_Max);
      __HAL_DMA_DISABLE_IT(this->huart->hdmarx,DMA_IT_HT);  // 关闭半传输中断,防止半传输中断影响接收完成标志位和接收长度
      if(this->callback != NULL)
        this->callback(this);
#ifdef RING_BUFFER
    }
#endif
    }
    this = this->next;
  }
}


